<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html>
<head>
<title>How to do .NET on top of 3ds Max</title>
</head>
<body>
<h1>How to do .NET on top of 3ds Max</h1>

<p>For 3ds Max 2008, a .NET SDK was created to allow developers to create plug-ins using the Microsoft .NET Framework.  We, the 3ds max development team, believe that the .NET platform provides productivity advantages over traditional C++ development, including increased robustness, faster build times, easier backwards compatibility and better tool support.  The .NET Framework also provides relatively simple and straightforward interoperability between .NET code and traditional C++ code.  For this release, we developed the Scene Explorer feature entirely in .NET and we intend to do more in .NET in future releases.</p>

<p>This documented is intended as a short primer for doing .NET development on top of the 3ds Max SDK.  Some familiarity with 3ds Max plug-in development and the .NET platform is assumed.  The major concerns in creating a .NET-based plug-in are bridging between the new .NET code and the existing unmanaged, .NET-unaware or "native" C++ 3ds Max SDK, and setting up the plug-in to enable 3ds Max to load its modules at run time.</p>

<h2>The 3ds Max .NET SDK</h2>

<p>The 3ds Max .NET SDK consists of the .NET assemblies (DLLs) distributed with the 3ds Max SDK under maxsdk\assemblies and maxsdk\x64\assemblies.  These same assemblies are distributed in the 3ds Max application installation.  .NET code compiles down to Common Intermediate Language (CIL) into an assembly and then Just-In-Time Compiled (JIT) to native machine code.  The high-level language compiler only needs the assemblies to perform symbol and type checking.  Actual symbol resolution and linking is performed by the JIT compiler.  As a result, no header files or libs are needed for .NET compilation.</p>

<p>The current 3ds Max .NET SDK consists of these assemblies:
<ul>
   <li>CSharpUtilities.dll - generally applicable utility classes for .NET programming.</li>
   <li>ExplorerFramework.dll - underlying abstract explorer framework classes upon which the Scene Explorer is built.</li>
   <li>ManagedServices.dll - exposure of some general native 3ds Max SDK functionality to .NET.</li>
   <li>MaxCustomControls.dll - UI components built on the .NET Framework for use in 3ds Max.</li>
   <li>SceneExplorer.dll - Specializations of the ExplorerFramework components for the Scene Explorer and bindings to the native 3ds Max data set.</li>
</ul></p>

<h2>C++/CLI</h2>

<p>To enable interoperability between managed .NET code and native unmanaged C++ code, Microsoft created the C++/CLI language as a superset of native C++.  The C++/CLI compiler is available in Visual Studio 2005.  The C++/CLI Language Specification is published by ECMA International as Standard ECMA-372 and is available for download here: <a href="http://www.ecma-international.org/publications/standards/Ecma-372.htm">http://www.ecma-international.org/publications/standards/Ecma-372.htm</a>.  Section 8. Language Overview provides a useful introduction to the language.  The rest of the Specification is useful as a reference.</p>

<p>Developers must use C++/CLI to make calls into the native 3ds Max SDK from a managed project.  Developers may also use C++/CLI to wrap native types in managed objects and export these managed wrapper classes outside their assembly.  Such exported types may be called from any .NET language: C#, C++/CLI, Visual Basic, etc.</p>

<p>The .NET Framework classes can be used from C++/CLI similarly to how they are used in C#.  Developers familiar with native C++ and C# will find that their C# experience carries over easily to C++/CLI.  Any good documentation on the .NET platform will be useful for C++/CLI work.</p>

<h2>Creating a C++ / CLI plug-in to be loaded by 3ds Max</h2>

<p>Currently, the 3ds Max plug-in loader can only load native DLLs as plug-ins.  To create a C++/CLI plug-in, we follow the same procedure as for a native plug-in documented elsewhere in the SDK documentation.  The major difference is that the CLR language extensions must be turned on in the project settings, under:</p>

<pre>Configuration Properties
    - General
       - Common Language Runtime Support: Common Language Runtime Support /clr</pre>

<p>Additionally to refer to the .NET 3ds Max SDK components, a #using reference path must be configured in the project settings.  These components are in the maxsdk\assemblies and maxsdk\x64\assemblies folders.  The sample C++ / CLI project is set as:</p>

<pre>Configuration Properties
   - C / C++
      - General
         - Resolve #using References: ..\..\assemblies</pre>

<p>The 3ds Max SDK installation includes a sample how-to plug-in demonstrating some of the fundamental issues involved in creating a .NET plug-in for 3ds Max.  The plug-in project file and code is in maxsdk\howto\SceneExplorerExtension.  This plug-in is loaded as a General Utility Plug-in through the native plug-in mechanism, then makes calls into managed code to add a new "Hit Points" property column to the Scene Explorer.</p>

<p>The code in dllmain.cpp, plus the SceneExplorerExtensionClassDesc and SceneExplorerExtensionGup classes use unmanaged code to fulfil the requirements for this module to be loaded as a General Utility Plug-in.  HitPointsProperty is a managed "ref" class.  It derives from a managed class in the SceneExplorer assembly and interacts with both native and managed objects.</p>

<h2>UI Compatibility</h2>

<p>As demonstrated by the Scene Explorer feature, it is possible to open managed .NET Windows on top of 3ds Max.  There were some compatibility issues in handling windowing events correctly between the native 3ds Max application and any managed Forms opened from it.  We have encapsulated the specialized handling required to work around these compatibility issues in the MaxCustomControls.MaxForm class, a subclass of System.Windows.Forms.Form.  To benefit from this compatibility support, any .NET plug-ins using a managed UI should host their controls in a MaxForm instead of the .NET Framework standard Form class.</p>

<p>Please note that this compatibility support has only be implemented at the Form level.  3ds Max does not currently explicitly support hosting .NET UI controls directly in the 3ds Max application window.  Any plug-in using managed UI controls should host them in a MaxForm window.</p>

<h2>Troubleshooting</h2>

<p>This section contains a list of common problems encountered when developing 3ds Max plug-ins in .NET.  These particular issues tend to have limited documentation available and were more difficult to debug or resolve.  Other common issues are not listed here because they are covered by resources elsewhere: either in the MSDN documentation, in the 3ds Max SDK documentation, or in most decent .NET books.</p>

<h3>Warning LNK4248: unresolved typeref token for 'type.'</h3>
<p>Generally warning LNK4248 is signalled when the C++ / CLI compiler must create an empty class wrapper for a forward-declared type.  This is not a problem if the class definition is not required in the module.  The warning can be suppressed by either including the full class definition for the given type or by explicitly creating an empty class definition. See SceneExplorerExtensionClassDesc.cpp in the how-to sample project for an example.</p>

<h3>3ds Max hangs or crashes on start-up while loading a .NET plug-in, or
3ds Max consistently hangs or crashes on exit after a session in which a .Net plug-in was loaded.</h3>
<p>Max will crash or hang if an exception is thrown during DLL loading or unloading.  For .NET plug-ins, an exception will be thrown for any call into managed code before the CLR is loaded or after the CLR was unloaded for the given DLL.  In other words, an exception will be thrown for any managed code executed from DllMain.  The solution is to perform any necessary initialization in LibInitialize and any necessary clean-up in LibShutdown.  Often a call to the CLR will occur for any static object instance in the DLL. Temporarily setting the /NOENTRY linker option under Configuration Properties -> Linker -> Advanced -> No Entry Point will help find any statically allocated instances.  See LibShutdown in dllmain.cpp in the how-to sample project for an example.</p>

<h3>Error C3767: 'function': candidate function(s) not accessible.</h3>
<p>Native types are private by default in C++/CLI.  If a public function takes a private type as a parameter, references to this function from outside the assembly will generate error C3767.  The solution is to mark the native type as public using a #pragma make_public(type) directive.  See NativeInclude.h in the how-to sample project for an example.</p>

<h3>Error LNK2022: metadata operation failed (80131195) : Custom attributes are not consistent</h3>
<p>This linker error will occur if a custom metadata attribute is applied consistently to a type.  This will occur if a native type is marked using a #pragma make_public directive in one translation unit, but included without the directive in another translation unit.  To avoid this problem, the 3ds Max SDK developers have adopted the habit of grouping all of a C++/CLI assembly's native includes in a single header file and applying any metadata directives in that file.  See NativeInclude.h in the how-to sample project for an example.</p>
</body>
</html>
